-- Desc: sku库存操作

-- 库存操作： 1.插入库存 2.增加库存 3.减少库存 4.替换库存(覆盖) 5.更新库存状态 具体操作可以查看 6.sku区域库存状态变更 LuaOperateEnum
-- sku库存状态： 0:商品或sku下架、禁用、删除    1:上架状态 提交订单时需要校验此状态    2：下线sku区域库存，同时减少sku总库存
-- KEYS入参格式： skuId_仓库id_库存变动数量_库存状态_库存操作, skuId_仓库id_库存变动数量_库存状态_库存操作...
-- params格式： {{skuId, 仓库id, 库存变动数量, 库存状态, 库存操作}, {skuId, 仓库id, 库存变动数量, 库存状态, 库存操作}}
-- sku总库存key格式-redis： mall4j_product:sku_sock:skuId
-- sku区域库存key格式-redis： mall4j_product:sku_sock:skuId_仓库id
-- sku库存value格式-redis： 可售库存数量_锁定库存数量_销量_sku库存状态

-- 处理流程：
-- 步骤一  将参数字符串(KEYS)拆分为数组(params)， 拆分过程中根据库存操作类型处理：
--        拼接区域库存和总库存的key（mgetSkuKeys：用于批量查询库存数据。 总库存key格式  key名称:skuId;  区域库存key格式 key名称:skuId_仓库id）,
--          skuKey：根据数组中的存放的skuId以及仓库id，与批量获取的数据进行匹配（redis mget响应数据顺序对应查询时的key顺序）
--          skuKey中的数据有两种类型，一种是字符类型代表区域库存的skuId和仓库id，数据格式为： skuId_仓库id; 一种是数字类型代表总库存的skuId, 数据格式为：skuId
--        库存操作：1.插入库存 5.更新库存状态 仅拼接总库存，不拼接区域库存的key
-- 步骤二  批量查询库存信息， 使用步骤一中拼接的key数组（mgetSkuKeys）进行查询
-- 步骤三  拆分步骤三mget获取的sku库存信息：
--            mget获取的sku库存信息格式为：可售库存数量_锁定库存数量_销量_sku库存状态
--            拆分成数组（skuStock）后的格式为：{可售库存数量, 锁定库存数量, 销量,sku库存状态}
--            库存集合（skuStocks）的数据格式为：map<skukey, sku库存数据>， 其中skukey还可以细分 -- 区域库存：skuId_仓库id（字符串）; 总库存：skuId（数字类型）
-- 步骤四  根据操作类型，对sku库存和状态进行计算或者替换
--            1.增加库存（操作类型 param[5] == '2'）， 根据库存变动数量（param[3]）增加区域库存（skuPointStock）、总库存（skuStock）的可售库存数量
--            2.增加库存（操作类型 param[5] == '2'）， 根据库存变动数量（param[3]）增加区域库存（skuPointStock）、总库存（skuStock）的可售库存数量
--            3.减少库存（操作类型 param[5] == '3'）， 根据库存变动数量（param[3]）减少区域库存（skuPointStock）、总库存（skuStock）的可售库存数量
--            4.替换库存（操作类型 param[5] == '4'）， 库存变动数量（param[3]）替换区域库存（skuPointStock）的可售库存数量，同时增加或者减少对应总库存（skuStock）的可售库存数量
--            5.更新库存状态（操作类型 param[5] == '5'）， 库存状态（param[4]）替换总库存（skuStock）的库存状态（skuStock[4]），区域库存状态没有使用，不需要更改
--            6.sku区域库存状态变更 0:下架此库存，并扣除sku总库存 1:上架此区域库存，并增加sku总库存数量
-- 步骤五  拼接将要更新的sku库存key、value， 以及sku库存变动结果（result）
-- 步骤六  批量保存修改后的sku库存信息


-- 参数数组
local params = {}
-- sku键数组
local skuKey = {}
-- 批量插入的sku库存key、value数组
local msetInsert = {}
-- 批量获取sku库存信息的key数组
local mgetSkuKeys = {}
-- sku库存操作结果   格式：  skuId_仓库id_剩余库存,skuId_仓库id_剩余库存...
local result = ""
-- 步骤一
for index, paramStr in ipairs(KEYS) do
    -- 拆分传入的参数, 格式： {skuId, 仓库id, 库存变动数量, 库存状态, 库存操作}
    local param = {}
    -- 匹配所有数字
    for value in string.gmatch(paramStr, "%d+") do
        table.insert(param, value)
    end

    -- 库存操作： 2.增加库存 3.减少库存 4.替换库存(覆盖) 5.更新库存状态
    -- 更新操作才需要将param放到params数组
    table.insert(params, param)

    -- 避免一个sku同时进行多个操作时，查询和计算的库存数据操作，所以这里要对区域库存和总库存的key去重
    local key = param[1].."_"..param[2]
    -- 操作总库存（修改状态）或者插入sku区域库存， 将key设为nil
    if param[2] == '0' or param[5] == '1' then
        key = nil
    end

    local skuId = tonumber(param[1])
    local notExistsKey = true
    local notExistsSku = true

    for skuIndex, value in ipairs(skuKey) do
        if not notExistsKey and not notExistsSku then break end
        if key == nil or key == value then
            notExistsKey = false
        end
        if skuId == value then
            notExistsSku = false
        end
    end

    -- 数组没有的sku数组再添加到数组，skuKey用于批量查询后的sku数据绑定
    if key ~= nil and notExistsKey then
        -- sku区域库存key格式： key名称:skuId_仓库id
        table.insert(mgetSkuKeys, "mall4j_product:sku_sock:"..key)
        table.insert(skuKey, key)
    end
    --总库存
    if notExistsSku then
        -- sku总库存key格式： key名称:skuId
        table.insert(mgetSkuKeys, "mall4j_product:sku_sock:"..param[1])
        -- 用数字类型区分总库存
        table.insert(skuKey, skuId)
    end
end

-- ================  步骤二    通过mget获取所有key的值  ================
-- call后面是可变长参数，unpack是将数组变成可变长参数
local skuStr = redis.call('mget', unpack(mgetSkuKeys));


-- ================  步骤三    拆分mget中获取到的sku库存信息  ================
-- skuStocks： map<skukey, sku库存数据>   skukey：区域库存=skuId_仓库id; 总库存=skuId
local skuStocks = {}
for index, key in ipairs(skuKey) do
    -- {可售库存数量, 锁定库存数量, 销量, sku库存状态}
    local values = skuStr[index]
    if values then
        local skuStock = {}
        for value in string.gmatch(values, "%d+") do
            table.insert(skuStock, tonumber(value))
        end
        -- 库存信息
        skuStocks[key] = skuStock
    end
end


-- ================  步骤四    根据操作类型，对sku库存和状态进行计算或者替换  ================
for i = 1,#params do
    -- skuId_仓库id_库存变动数量_库存状态_库存操作
    local param = params[i]
    local skuPointStock = skuStocks[param[1].."_"..param[2]]
    local skuStock = skuStocks[tonumber(param[1])]

    -- 库存变动数量
    local stockChange = tonumber(param[3])
    -- 库存操作： 1.插入库存 2.增加库存 3.减少库存 4.替换库存(覆盖) 5.更新库存状态
    if param[5] == '1' then
        if not skuStock then
            skuStock = {0,0,0,1}
            skuStocks[tonumber(param[1])] = skuStock
        end
        skuPointStock = {param[3], 0, 0, param[4]}
        skuStock[1] = tonumber(skuStock[1]) + tonumber(skuPointStock[1])
        skuStocks[param[1].."_"..param[2]] = skuPointStock
        table.insert(skuKey, param[1].."_"..param[2])

    elseif param[5] == '2' then
        -- 2.增加库存:  可售库存 = 可售库存 + 库存变动数量
        skuPointStock[1] = skuPointStock[1] + stockChange
        skuStock[1] = skuStock[1] + stockChange

    elseif param[5] == '3' then
        -- 3.减少库存:  可售库存 = 可售库存 - 变化库存
        skuPointStock[1] = skuPointStock[1] - stockChange
        skuStock[1] = skuStock[1] - stockChange
        -- 减少库存需要判断可售库存数量是否足够扣除
        if skuPointStock[1] < 0 then
            -- 库存改变后小于0 返回skuId
            return param[1]
        end

    elseif param[5] == '4' then
        -- 4.替换库存(覆盖):  可售库存 = 变化库存
        -- sku总库存 = sku原总库存 + 变化的区域库存(新的区域库存 - 旧的区域库存)
        skuStock[1] = tonumber(skuStock[1]) + stockChange - tonumber(skuPointStock[1])
        -- sku区域库存 - 等计算完再赋值
        skuPointStock[1] = stockChange

    elseif param[5] == '5' then
        -- 5.更新库存状态: 替换sku状态
        skuStock[4] = param[4]

    elseif param[5] == '6' then
        -- 6.sku区域库存状态变更 0:下架此库存，并扣除sku总库存 1:上架此区域库存，并增加sku总库存数量
        -- 状态不一样才需要更改
        if skuPointStock[4] ~= tonumber(param[4]) then
            -- 下架区域库存： 区域库存状态设置为0，
            if param[4] == '0' then
                skuStock[1] = tonumber(skuStock[1]) - tonumber(skuPointStock[1])
                skuPointStock[4] = 0
            elseif param[4] == '1' then
                skuStock[1] = tonumber(skuStock[1]) + tonumber(skuPointStock[1])
                skuPointStock[4] = 1
            end
        end
    end
end


-- ================  步骤五    添加sku库存的key、value  ================
local msetSkuKV = {}
for index, key in ipairs(skuKey) do
    -- 库存信息
    local skuStock = skuStocks[key]
    -- mset sku库存key     格式：  key名称:skuId 或者 key名称:skuId_仓库id
    table.insert(msetSkuKV, "mall4j_product:sku_sock:"..key)
    -- mset sku库存value   格式：  可售库存数量_锁定库存数量_销量_sku库存状态
    table.insert(msetSkuKV, skuStock[1].."_"..skuStock[2].."_"..skuStock[3].."_"..skuStock[4])
    -- skuId_仓库id_剩余库存,skuId_仓库id_剩余库存...
    if type(key) == 'number' then
        -- 总库存日志
        result = result..key.."_0_"..skuStock[1]..","
    else
        -- 区域库存日志
        result = result..key.."_"..skuStock[1]..","
    end
end


-- ================  步骤六    执行mset批量保存修改后的sku库存信息  ================
redis.call('mset',unpack(msetSkuKV))

return result
